﻿using System;
using System.Collections.Generic;
using System.Reflection;
using CoreEx.Common.Proxy;
using LinFu.AOP.Interfaces;
using LinFu.IoC.Configuration;
using LinFu.Proxy.Interfaces;
using CoreEx.DynamicInvoker;
using Mono.Cecil;
using Mono.Cecil.Cil;
using CoreEx.Common.Extensions;
using LinFu.IoC;

namespace CoreEx.Common.Implementation.Proxy
{
    /// <summary>
    /// Intercepts calls to the <see cref="IProxyBuilder.Construct"/> method  
    /// to allow the <see cref="IProxy"/> type to be modified.
    /// </summary>
    [Implements(typeof(IInvokeWrapper), ServiceName = "ProxyBuilderInterceptor")]
    public class ProxyBuilderInterceptor : IInvokeWrapper, IInitialize
    {
        private readonly IProxyBuilder _actualProxyBuilder;
        private IEnumerable<ITypeModifier> _modificators;


        public ProxyBuilderInterceptor(IProxyBuilder actualProxyBuilder)
        {
            _actualProxyBuilder = actualProxyBuilder;
        }

        public void BeforeInvoke(IInvocationInfo info)
        {
            var originalBaseType = (Type)info.Arguments[0];
            var baseInterfaces = (IEnumerable<Type>)info.Arguments[1];
            var moduleDefinition = (ModuleDefinition)info.Arguments[2];
            var typeDefinition = (TypeDefinition)info.Arguments[3];
            _modificators.ForEach(m => m.BeforeConstruction(originalBaseType, baseInterfaces, moduleDefinition, typeDefinition));
        }
        

        public void AfterInvoke(IInvocationInfo info, object returnValue)
        {                     
            //Get a reference to the typedefinition that has been created

            var originalBaseType = (Type)info.Arguments[0];
            var baseInterfaces = (IEnumerable <Type>)info.Arguments[1];
            var moduleDefinition = (ModuleDefinition) info.Arguments[2];
            var typeDefinition = (TypeDefinition)info.Arguments[3];           
            _modificators.ForEach(m => m.AfterConstruction(originalBaseType,baseInterfaces,moduleDefinition,typeDefinition));
        }

        

        public object DoInvoke(IInvocationInfo info)    
        {
            //Allow the actual method to run 
            return info.TargetMethod.DynamicInvoke(_actualProxyBuilder, info.Arguments);
        }



        #region IInitialize<IServiceContainer> Members

        public void Initialize(LinFu.IoC.Interfaces.IServiceContainer source)
        {
            _modificators = source.GetServices<ITypeModifier>();
        }

        #endregion
    }
}